import tkinter as tk
from tkinter import ttk, filedialog, messagebox, scrolledtext
import requests
import json
import os
import threading
import re
import base64
from urllib.parse import urlparse
from PIL import Image, ImageTk
import io

class RepoDownloader:
    def __init__(self, root):
        self.root = root
        self.root.title("代码仓库文件下载器-雨夜")
        self.root.geometry("900x600")
        self.root.minsize(800, 500)
        
        # 当前平台（默认为GitHub）
        self.current_platform = tk.StringVar(value="GitHub")
        
        # 设置样式 - 使用更兼容的方式设置样式
        self.style = ttk.Style()
        # 在Windows上，某些样式属性可能不起作用，所以我们使用更通用的方法
        try:
            self.style.configure("TFrame", background="#FFFFFF")
            self.style.configure("TButton", padding=6, font=('Arial', 10), foreground="black")
            self.style.configure("TLabel", font=('Arial', 10), foreground="black")
            self.style.configure("Header.TLabel", font=('Arial', 12), foreground="black")
            
            # 创建自定义按钮样式，确保文字颜色更明显
            self.style.configure("Custom.TButton", 
                                 padding=6, 
                                 font=('Arial', 10),
                                 background="#e1e1e1", 
                                 foreground="black")
            
            self.style.map("Custom.TButton",
                background=[('active', "#d9d9d9"), ('pressed', "#c0c0c0"), ('!disabled', "#e1e1e1")],
                foreground=[('active', 'black'), ('pressed', 'black'), ('!disabled', 'black')])
        except Exception as e:
            # 如果样式设置失败，使用默认样式
            print(f"样式设置失败: {str(e)}")
            pass
        
        # 创建主框架
        self.main_frame = ttk.Frame(root, padding="10")
        self.main_frame.pack(fill=tk.BOTH, expand=True)
        
        # 创建顶部框架
        self.top_frame = ttk.Frame(self.main_frame)
        self.top_frame.pack(fill=tk.X, pady=10)
        
        # 平台选择
        ttk.Label(self.top_frame, text="平台:", style="Header.TLabel").pack(side=tk.LEFT, padx=5)
        self.platform_combo = ttk.Combobox(self.top_frame, width=10, values=["GitHub", "Gitee"], textvariable=self.current_platform)
        self.platform_combo.pack(side=tk.LEFT, padx=5)
        self.platform_combo.bind("<<ComboboxSelected>>", self.update_resource_list)
        
        # 仓库URL输入 - 使用Combobox替代Entry以支持下拉列表
        ttk.Label(self.top_frame, text="仓库URL:", style="Header.TLabel").pack(side=tk.LEFT, padx=5)
        
        # 创建带有备注的GitHub资源列表
        self.github_resources = [
            "https://github.com/username/repository",
            "https://github.com/Chiowe/ChinaTextbook- 【学生课本】",
            "https://github.com/521xueweihan/HelloGitHub - 【GitHub优质项目】",
            "https://github.com/kon9chunkit/GitHub-Chinese-Top-Charts - 【中文项目排行榜】",
            "https://github.com/justjavac/free-programming-books-zh_CN - 【免费编程书籍】",
            "https://github.com/chinese-poetry/chinese-poetry - 【中国古诗词数据库】",
            "https://github.com/d2l-ai/d2l-zh - 【动手学深度学习】",
            "https://github.com/scutan90/DeepLearning-500-questions - 【深度学习500问】",
            "https://github.com/jackfrued/Python-100-Days - 【Python 100天】",
            "https://github.com/shimohq/chinese-programmer-wrong-pronunciation - 【程序员容易读错的单词】"
        ]
        
        # 创建带有备注的Gitee资源列表
        self.gitee_resources = [
            "https://gitee.com/username/repository",
            "https://gitee.com/dotnetchina/OpenAuth.Net - 【权限管理系统】",
            "https://gitee.com/dromara/hutool - 【Java工具集】",
            "https://gitee.com/liyupi/sql-father-frontend-public - 【SQL之父】",
            "https://gitee.com/jeecg/jeecg-boot - 【低代码开发平台】",
            "https://gitee.com/pig4cloud/pig - 【微服务开发平台】",
            "https://gitee.com/zhengqingya/java-workspace - 【Java学习】",
            "https://gitee.com/dromara/Sa-Token - 【权限认证框架】",
            "https://gitee.com/xuxueli0323/xxl-job - 【分布式任务调度平台】",
            "https://gitee.com/mindskip/xzs - 【在线考试系统】"
        ]
        
        # 默认使用GitHub资源
        self.current_resources = self.github_resources
        self.url_entry = ttk.Combobox(self.top_frame, width=50, values=self.current_resources)
        self.url_entry.pack(side=tk.LEFT, padx=5, fill=tk.X, expand=True)
        self.url_entry.insert(0, "https://github.com/username/repository")
        self.url_entry.bind("<FocusIn>", self.clear_default_text)
        
        # 解析按钮 - 使用自定义样式
        self.parse_button = ttk.Button(self.top_frame, text="解析仓库", command=self.parse_repository, style="Custom.TButton")
        self.parse_button.pack(side=tk.LEFT, padx=5)
        
        # 创建中间框架
        self.middle_frame = ttk.Frame(self.main_frame)
        self.middle_frame.pack(fill=tk.BOTH, expand=True, pady=10)
        
        # 创建路径导航框架
        self.path_frame = ttk.Frame(self.middle_frame)
        self.path_frame.pack(fill=tk.X, pady=(0, 10))
        
        ttk.Label(self.path_frame, text="当前路径:", style="Header.TLabel").pack(side=tk.LEFT, padx=5)
        self.path_var = tk.StringVar()
        self.path_var.set("/")
        self.path_label = ttk.Label(self.path_frame, textvariable=self.path_var)
        self.path_label.pack(side=tk.LEFT, padx=5, fill=tk.X, expand=True)
        
        # 返回上级按钮 - 使用自定义样式
        self.back_button = ttk.Button(self.path_frame, text="返回上级", command=self.go_back, state=tk.DISABLED, style="Custom.TButton")
        self.back_button.pack(side=tk.RIGHT, padx=5)
        
        # 创建文件列表框架
        self.list_frame = ttk.Frame(self.middle_frame)
        self.list_frame.pack(fill=tk.BOTH, expand=True)
        
        # 创建文件列表
        self.columns = ("name", "type", "size", "last_commit")
        self.file_tree = ttk.Treeview(self.list_frame, columns=self.columns, show="headings", selectmode="extended")
        
        # 定义列
        self.file_tree.heading("name", text="名称")
        self.file_tree.heading("type", text="类型")
        self.file_tree.heading("size", text="大小")
        self.file_tree.heading("last_commit", text="最后提交")
        
        self.file_tree.column("name", width=300)
        self.file_tree.column("type", width=100)
        self.file_tree.column("size", width=100)
        self.file_tree.column("last_commit", width=200)
        
        # 添加滚动条
        self.scrollbar = ttk.Scrollbar(self.list_frame, orient=tk.VERTICAL, command=self.file_tree.yview)
        self.file_tree.configure(yscroll=self.scrollbar.set)
        
        self.file_tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        self.scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
        
        # 绑定双击事件
        self.file_tree.bind("<Double-1>", self.on_item_double_click)
        self.file_tree.bind("<ButtonRelease-1>", self.on_item_select)
        
        # 创建底部框架
        self.bottom_frame = ttk.Frame(self.main_frame)
        self.bottom_frame.pack(fill=tk.X, pady=10)
        
        # 选择保存位置
        ttk.Label(self.bottom_frame, text="保存位置:", style="Header.TLabel").pack(side=tk.LEFT, padx=5)
        self.save_path_var = tk.StringVar()
        self.save_path_var.set(os.path.expanduser("~\\Downloads"))
        self.save_path_entry = ttk.Entry(self.bottom_frame, textvariable=self.save_path_var, width=50)
        self.save_path_entry.pack(side=tk.LEFT, padx=5, fill=tk.X, expand=True)
        
        self.browse_button = ttk.Button(self.bottom_frame, text="浏览...", command=self.browse_save_location, style="Custom.TButton")
        self.browse_button.pack(side=tk.LEFT, padx=5)
        
        # 下载按钮
        self.download_button = ttk.Button(self.bottom_frame, text="下载选中文件", command=self.download_selected, state=tk.DISABLED, style="Custom.TButton")
        self.download_button.pack(side=tk.RIGHT, padx=5)
        
        # 创建状态框架
        self.status_frame = ttk.Frame(self.main_frame)
        self.status_frame.pack(fill=tk.X, pady=(0, 10))
        
        # 状态标签
        self.status_var = tk.StringVar()
        self.status_var.set("准备就绪")
        self.status_label = ttk.Label(self.status_frame, textvariable=self.status_var)
        self.status_label.pack(side=tk.LEFT, padx=5)
        
        # 进度条
        self.progress = ttk.Progressbar(self.status_frame, orient=tk.HORIZONTAL, length=300, mode='determinate')
        self.progress.pack(side=tk.RIGHT, padx=5, fill=tk.X, expand=True)
        
        # 存储仓库信息
        self.repo_owner = ""
        self.repo_name = ""
        self.current_path = ""
        self.current_branch = ""  # 将在解析仓库时确定
        self.default_branch = ""  # 存储仓库的默认分支
        self.path_history = []
        self.selected_items = []
        
        # 存储文件内容缓存
        self.file_cache = {}
        
        # GitHub API请求头 - 添加User-Agent以符合GitHub API要求
        self.headers = {
            'User-Agent': 'GitHub-File-Downloader-App',
            # 如果有GitHub令牌，可以在这里添加Authorization头
            # 'Authorization': 'token YOUR_GITHUB_TOKEN'
        }
        
    def update_resource_list(self, event=None):
        """根据选择的平台更新资源列表"""
        platform = self.current_platform.get()
        
        # 清除当前输入
        current_url = self.url_entry.get()
        self.url_entry.delete(0, tk.END)
        
        # 根据平台选择资源列表
        if platform == "GitHub":
            self.current_resources = self.github_resources
            default_url = "https://github.com/username/repository"
        else:  # Gitee
            self.current_resources = self.gitee_resources
            default_url = "https://gitee.com/username/repository"
        
        # 更新下拉列表
        self.url_entry['values'] = self.current_resources
        
        # 如果当前URL不是默认URL，保留它
        if current_url and current_url not in [
            "https://github.com/username/repository", 
            "https://gitee.com/username/repository"
        ]:
            self.url_entry.insert(0, current_url)
        else:
            self.url_entry.insert(0, default_url)
    
    def clear_default_text(self, event):
        current_text = self.url_entry.get()
        if current_text == "https://github.com/username/repository" or current_text == "https://gitee.com/username/repository":
            self.url_entry.delete(0, tk.END)
    
    def parse_repository(self):
        url = self.url_entry.get().strip()
        if not url:
            messagebox.showerror("错误", "请输入仓库URL")
            return
        
        # 处理带有备注的URL（例如：https://github.com/username/repo - 【备注】）
        # 使用正则表达式更准确地识别URL和备注部分
        match = re.search(r'^(https?://[^\s【】]+)(?:\s+-\s+|\s+)【.+】', url)
        if match:
            url = match.group(1).strip()
        # 确保URL末尾的连字符"-"不会被移除
        elif " - " in url:
            url = url.split(" - ")[0].strip()
        
        # 确定平台
        platform = self.current_platform.get()
        
        # 解析URL
        try:
            parsed_url = urlparse(url)
            path_parts = parsed_url.path.strip('/').split('/')
            
            if len(path_parts) < 2:
                messagebox.showerror("错误", f"无效的{platform}仓库URL")
                return
            
            self.repo_owner = path_parts[0]
            self.repo_name = path_parts[1]
            
            # 检查是否指定了分支和路径
            self.current_path = ""
            specified_branch = None
            if len(path_parts) > 2:
                if path_parts[2] == "tree" and len(path_parts) > 3:
                    specified_branch = path_parts[3]
                    if len(path_parts) > 4:
                        self.current_path = '/'.join(path_parts[4:])
            
            self.path_history = []
            if self.current_path:
                path_parts = self.current_path.split('/')
                for i in range(len(path_parts)):
                    self.path_history.append('/'.join(path_parts[:i]))
            
            self.path_var.set(f"/{self.current_path}" if self.current_path else "/")
            
            # 获取仓库的默认分支
            self.status_var.set("正在获取仓库信息...")
            self.root.update_idletasks()
            
            platform = self.current_platform.get()
            
            try:
                # 根据平台使用不同的API获取仓库信息
                if platform == "GitHub":
                    # 使用GitHub API获取仓库信息
                    repo_api_url = f"https://api.github.com/repos/{self.repo_owner}/{self.repo_name}"
                else:  # Gitee
                    # 使用Gitee API获取仓库信息
                    repo_api_url = f"https://gitee.com/api/v5/repos/{self.repo_owner}/{self.repo_name}"
                
                response = requests.get(repo_api_url, headers=self.headers)
                
                if response.status_code == 200:
                    repo_info = response.json()
                    self.default_branch = repo_info.get('default_branch', 'master')  # Gitee默认通常是master
                    
                    # 如果URL中指定了分支，使用指定的分支，否则使用默认分支
                    self.current_branch = specified_branch if specified_branch else self.default_branch
                else:
                    # 如果API请求失败，使用指定的分支或默认为'master'（Gitee）或'main'（GitHub）
                    default_branch = 'main' if platform == "GitHub" else 'master'
                    self.current_branch = specified_branch if specified_branch else default_branch
                    self.default_branch = default_branch
                    
                    if response.status_code == 403:
                        if platform == "GitHub" and 'rate limit exceeded' in response.text.lower():
                            messagebox.showwarning("警告", "GitHub API速率限制已达到，部分功能可能受限。\n请稍后再试或考虑添加GitHub令牌。")
                        else:
                            messagebox.showwarning("警告", f"{platform} API访问受限，部分功能可能不可用。")
                    elif response.status_code == 404:
                        messagebox.showwarning("警告", f"找不到仓库: {self.repo_owner}/{self.repo_name}\n请检查URL是否正确。")
            except Exception as e:
                # 如果获取默认分支失败，使用指定的分支或默认为'master'（Gitee）或'main'（GitHub）
                default_branch = 'main' if platform == "GitHub" else 'master'
                self.current_branch = specified_branch if specified_branch else default_branch
                self.default_branch = default_branch
                print(f"获取仓库信息时出错: {str(e)}")
            
            # 获取仓库内容
            self.get_repository_contents()
            
        except Exception as e:
            messagebox.showerror("错误", f"解析仓库URL时出错: {str(e)}")
    
    def get_repository_contents(self):
        self.status_var.set("正在获取仓库内容...")
        self.root.update_idletasks()
        
        # 清空文件列表
        for item in self.file_tree.get_children():
            self.file_tree.delete(item)
        
        try:
            platform = self.current_platform.get()
            
            # 根据平台构建API URL
            if platform == "GitHub":
                # GitHub API
                api_url = f"https://api.github.com/repos/{self.repo_owner}/{self.repo_name}/contents"
                if self.current_path:
                    api_url += f"/{self.current_path}"
                
                if self.current_branch != self.default_branch:
                    api_url += f"?ref={self.current_branch}"
            else:  # Gitee
                # Gitee API
                api_url = f"https://gitee.com/api/v5/repos/{self.repo_owner}/{self.repo_name}/contents"
                if self.current_path:
                    api_url += f"/{self.current_path}"
                
                # Gitee API需要添加ref参数
                api_url += f"?ref={self.current_branch}"
            
            # 发送请求
            response = requests.get(api_url, headers=self.headers)
            if response.status_code != 200:
                if response.status_code == 404:
                    messagebox.showerror("错误", "找不到指定的仓库或路径")
                else:
                    messagebox.showerror("错误", f"获取仓库内容失败: {response.status_code}")
                self.status_var.set("获取仓库内容失败")
                return
            
            # 解析响应
            contents = response.json()
            
            # 检查是否为列表（目录）或单个文件
            if isinstance(contents, dict):
                # 单个文件
                self.display_file_content(contents)
                return
            
            # 显示目录内容
            for item in sorted(contents, key=lambda x: (x['type'] != 'dir', x['name'].lower())):
                item_name = item['name']
                item_type = "文件夹" if item['type'] == 'dir' else "文件"
                
                # 安全地获取文件大小，Gitee可能返回None
                if item['type'] == 'dir':
                    item_size = "-"
                else:
                    # 确保size字段存在且不为None
                    size_value = item.get('size')
                    if size_value is not None:
                        item_size = self.format_size(size_value)
                    else:
                        item_size = "未知"
                
                # 获取最后提交信息
                last_commit = self.get_last_commit(item['path'])
                
                # 确保download_url存在，Gitee可能不提供此字段
                download_url = item.get('download_url', '')
                
                self.file_tree.insert("", tk.END, values=(item_name, item_type, item_size, last_commit),
                                     tags=(item['type'], item['path'], download_url))
            
            # 更新状态
            self.status_var.set(f"已加载 {len(contents)} 个项目")
            
            # 更新返回按钮状态
            self.back_button['state'] = tk.NORMAL if self.path_history else tk.DISABLED
            
        except Exception as e:
            messagebox.showerror("错误", f"获取仓库内容时出错: {str(e)}")
            self.status_var.set("获取仓库内容失败")
    
    def get_last_commit(self, path):
        # 实际应用中，可以调用GitHub API获取最后提交信息
        # 但这需要额外的API调用，可能会导致速率限制
        # 这里简化处理，返回占位符
        return "-"
    
    def format_size(self, size_bytes):
        # 格式化文件大小
        # 确保size_bytes不是None
        if size_bytes is None:
            return "未知"
            
        if size_bytes < 1024:
            return f"{size_bytes} B"
        elif size_bytes < 1024 * 1024:
            return f"{size_bytes / 1024:.1f} KB"
        elif size_bytes < 1024 * 1024 * 1024:
            return f"{size_bytes / (1024 * 1024):.1f} MB"
        else:
            return f"{size_bytes / (1024 * 1024 * 1024):.1f} GB"
    
    def on_item_double_click(self, event):
        # 获取选中的项目
        selection = self.file_tree.selection()
        if not selection:
            return
        
        item = selection[0]
        item_values = self.file_tree.item(item, "values")
        item_tags = self.file_tree.item(item, "tags")
        
        # 检查是否为文件夹
        if item_tags[0] == 'dir':
            # 保存当前路径到历史记录
            if self.current_path:
                self.path_history.append(self.current_path)
            
            # 更新当前路径
            self.current_path = item_tags[1]
            self.path_var.set(f"/{self.current_path}")
            
            # 获取新路径的内容
            self.get_repository_contents()
    
    def on_item_select(self, event):
        # 获取选中的项目
        selection = self.file_tree.selection()
        self.selected_items = selection
        
        # 更新下载按钮状态
        self.download_button['state'] = tk.NORMAL if selection else tk.DISABLED
    
    def go_back(self):
        if not self.path_history:
            return
        
        # 获取上一级路径
        self.current_path = self.path_history.pop() if self.path_history else ""
        self.path_var.set(f"/{self.current_path}" if self.current_path else "/")
        
        # 获取新路径的内容
        self.get_repository_contents()
    
    def browse_save_location(self):
        directory = filedialog.askdirectory(initialdir=self.save_path_var.get())
        if directory:
            self.save_path_var.set(directory)
    
    def download_selected(self):
        if not self.selected_items:
            messagebox.showinfo("提示", "请选择要下载的文件或文件夹")
            return
        
        save_path = self.save_path_var.get()
        if not os.path.isdir(save_path):
            try:
                os.makedirs(save_path)
            except Exception as e:
                messagebox.showerror("错误", f"创建保存目录失败: {str(e)}")
                return
        
        # 开始下载
        self.status_var.set("准备下载...")
        self.progress['value'] = 0
        self.root.update_idletasks()
        
        # 使用线程进行下载，避免阻塞GUI
        threading.Thread(target=self.download_items, args=(self.selected_items, save_path)).start()
    
    def download_items(self, items, save_path):
        total_items = len(items)
        completed_items = 0
        
        for item in items:
            item_values = self.file_tree.item(item, "values")
            item_tags = self.file_tree.item(item, "tags")
            
            item_name = item_values[0]
            item_type = item_tags[0]
            item_path = item_tags[1]
            
            # 使用lambda捕获当前值，避免闭包问题
            self.root.after(0, lambda name=item_name: self.status_var.set(f"正在下载{'文件夹' if item_type == 'dir' else '文件'}: {name}"))
            self.root.after(0, self.root.update_idletasks)
            
            try:
                if item_type == 'dir':
                    # 下载文件夹
                    self.download_directory(item_path, os.path.join(save_path, item_name))
                else:
                    # 下载文件
                    download_url = item_tags[2]
                    if download_url:
                        self.download_file(download_url, os.path.join(save_path, item_name))
                    else:
                        # 如果没有直接的下载URL，使用API获取
                        self.download_file_by_api(item_path, os.path.join(save_path, item_name))
            
            except Exception as e:
                error_msg = f"下载 {item_name} 失败: {str(e)}"
                self.root.after(0, lambda msg=error_msg: messagebox.showerror("错误", msg))
            
            completed_items += 1
            progress_value = int((completed_items / total_items) * 100)
            
            # 使用lambda捕获当前进度值
            self.root.after(0, lambda v=progress_value: self.update_progress(v))
            # 短暂暂停，让UI有时间更新
            self.root.after(10)
        
        self.root.after(0, lambda: self.status_var.set("下载完成"))
        self.root.after(0, lambda: messagebox.showinfo("完成", "所有选定的文件已下载完成"))
    
    def update_progress(self, value):
        self.progress['value'] = value
        # 强制更新UI
        self.root.update()
    
    def download_directory(self, dir_path, save_path):
        # 创建目录
        if not os.path.exists(save_path):
            os.makedirs(save_path)
        
        platform = self.current_platform.get()
        
        # 根据平台构建API URL
        if platform == "GitHub":
            # GitHub API
            api_url = f"https://api.github.com/repos/{self.repo_owner}/{self.repo_name}/contents/{dir_path}"
            if self.current_branch != self.default_branch:
                api_url += f"?ref={self.current_branch}"
        else:  # Gitee
            # Gitee API
            api_url = f"https://gitee.com/api/v5/repos/{self.repo_owner}/{self.repo_name}/contents/{dir_path}"
            # Gitee API需要添加ref参数
            api_url += f"?ref={self.current_branch}"
        
        # 更新状态
        dir_name = os.path.basename(save_path)
        self.root.after(0, lambda: self.status_var.set(f"获取目录内容: {dir_name}"))
        self.root.after(0, self.root.update_idletasks)
        
        response = requests.get(api_url, headers=self.headers)
        if response.status_code != 200:
            raise Exception(f"获取目录内容失败: {response.status_code}")
        
        contents = response.json()
        
        # 递归下载目录内容
        for item in contents:
            item_name = item['name']
            item_path = item['path']
            item_type = item['type']
            
            # 更新状态
            self.root.after(0, lambda name=item_name: self.status_var.set(f"处理: {name}"))
            self.root.after(0, self.root.update_idletasks)
            
            if item_type == 'dir':
                # 递归下载子目录
                self.download_directory(item_path, os.path.join(save_path, item_name))
            else:
                # 下载文件
                download_url = item.get('download_url')
                # 更新状态
                self.root.after(0, lambda name=item_name: self.status_var.set(f"下载文件: {name}"))
                self.root.after(0, self.root.update_idletasks)
                
                if download_url:
                    self.download_file(download_url, os.path.join(save_path, item_name))
                else:
                    # 如果没有直接的下载URL，使用API获取
                    self.download_file_by_api(item_path, os.path.join(save_path, item_name))
                
                # 短暂暂停，让UI有时间更新
                self.root.after(10)
    
    def download_file(self, url, save_path):
        """下载文件到指定路径，处理文件冲突"""
        # 确保目标目录存在
        os.makedirs(os.path.dirname(save_path), exist_ok=True)
        
        file_name = os.path.basename(save_path)
        self.root.after(0, lambda name=file_name: self.status_var.set(f"下载文件: {name}"))
        self.root.after(0, self.root.update_idletasks)
        
        # 检查文件是否已存在
        if os.path.exists(save_path):
            # 使用线程安全的方式处理文件冲突
            choice = self.show_conflict_dialog(file_name)
            if choice == "cancel":
                return False
            elif choice == "rename":
                # 自动重命名文件
                save_path = self.get_unique_filename(save_path)
        
        return self._download_file_content(url, save_path)
    
    def show_conflict_dialog(self, filename):
        """显示文件冲突对话框（线程安全方式）"""
        result = {"choice": "overwrite"}  # 默认覆盖
        
        def show_dialog():
            response = messagebox.askquestion(
                "文件已存在",
                f"文件 {filename} 已存在。\n\n如何处理？",
                detail="选择'是'覆盖，'否'自动重命名，'取消'中止下载",
                type="yesnocancel"
            )
            result["choice"] = "overwrite" if response == "yes" else \
                             "rename" if response == "no" else "cancel"
        
        # 在主线程中执行对话框
        self.root.after(0, show_dialog)
        
        # 等待用户响应（非阻塞方式）
        while result["choice"] == "overwrite" and not self.root.quit:
            self.root.update()
            time.sleep(0.1)
        
        return result["choice"]
    
    def get_unique_filename(self, path):
        """为文件生成唯一名称，避免冲突"""
        base, ext = os.path.splitext(path)
        counter = 1
        while os.path.exists(f"{base}_{counter}{ext}"):
            counter += 1
        return f"{base}_{counter}{ext}"
    
    def _download_file_content(self, url, save_path):
        """实际下载文件内容的内部方法"""
        try:
            response = requests.get(url, headers=self.headers, stream=True)
            if response.status_code == 200:
                total_size = int(response.headers.get('content-length', 0))
                downloaded = 0
                
                with open(save_path, 'wb') as f:
                    for chunk in response.iter_content(chunk_size=8192):
                        if chunk:
                            f.write(chunk)
                            downloaded += len(chunk)
                            self.update_download_progress(file_name, downloaded, total_size)
                return True
            else:
                raise Exception(f"下载失败: {response.status_code}")
        except Exception as e:
            self.show_download_error(os.path.basename(save_path), str(e))
            return False
        
        try:
            # 下载文件
            response = requests.get(url, headers=self.headers, stream=True)
            if response.status_code == 200:
                # 获取文件大小（如果可用）
                total_size = int(response.headers.get('content-length', 0))
                downloaded = 0
                
                with open(save_path, 'wb') as f:
                    # 对于小文件，直接写入
                    if total_size < 102400:  # 小于100KB
                        f.write(response.content)
                    else:
                        # 对于大文件，使用流式下载
                        for chunk in response.iter_content(chunk_size=8192):
                            if chunk:
                                f.write(chunk)
                                downloaded += len(chunk)
                                
                                # 如果知道总大小，更新进度
                                if total_size > 0:
                                    progress = int((downloaded / total_size) * 100)
                                    # 每5%更新一次UI，避免过于频繁的更新
                                    if progress % 5 == 0:
                                        self.root.after(0, lambda p=progress, n=file_name: 
                                                      self.status_var.set(f"下载 {n}: {p}%"))
                                        self.root.after(0, self.root.update_idletasks)
            else:
                raise Exception(f"下载失败: {response.status_code}")
        except Exception as e:
            self.root.after(0, lambda msg=str(e): messagebox.showerror("下载错误", f"下载 {file_name} 失败: {msg}"))
            raise
    
    def download_file_by_api(self, file_path, save_path):
        platform = self.current_platform.get()
        
        # 根据平台构建API URL
        if platform == "GitHub":
            # 使用GitHub API获取文件内容
            api_url = f"https://api.github.com/repos/{self.repo_owner}/{self.repo_name}/contents/{file_path}"
            if self.current_branch != self.default_branch:
                api_url += f"?ref={self.current_branch}"
        else:  # Gitee
            # 使用Gitee API获取文件内容
            api_url = f"https://gitee.com/api/v5/repos/{self.repo_owner}/{self.repo_name}/contents/{file_path}"
            # Gitee API需要添加ref参数
            api_url += f"?ref={self.current_branch}"
        
        response = requests.get(api_url, headers=self.headers)
        if response.status_code != 200:
            raise Exception(f"获取文件内容失败: {response.status_code}")
        
        content = response.json()
        
        # 检查文件大小 - 确保size不是None
        file_size = content.get('size')
        if file_size is not None and file_size > 1024 * 1024:  # 大于1MB的文件
            # 对于大文件，使用特定平台的下载方法
            self.download_large_file(file_path, save_path)
            return
        
        # 确保content字段存在
        if 'content' not in content:
            # 如果没有content字段，尝试使用下载URL
            if platform == "GitHub":
                raw_url = f"https://raw.githubusercontent.com/{self.repo_owner}/{self.repo_name}/{self.current_branch}/{file_path}"
            else:  # Gitee
                raw_url = f"https://gitee.com/{self.repo_owner}/{self.repo_name}/raw/{self.current_branch}/{file_path}"
            
            self.download_file(raw_url, save_path)
            return
        
        # 解码Base64内容
        file_content = base64.b64decode(content['content'])
        
        # 确保目标目录存在
        os.makedirs(os.path.dirname(save_path), exist_ok=True)
        
        with open(save_path, 'wb') as f:
            f.write(file_content)
    
    def download_large_file(self, file_path, save_path):
        try:
            platform = self.current_platform.get()
            file_name = os.path.basename(file_path)
            
            # 确保目标目录存在
            os.makedirs(os.path.dirname(save_path), exist_ok=True)
            
            self.root.after(0, lambda name=file_name: self.status_var.set(f"正在下载大文件: {name}"))
            self.root.after(0, self.root.update_idletasks)
            
            if platform == "GitHub":
                # 对于GitHub，尝试直接从原始URL下载
                raw_url = f"https://raw.githubusercontent.com/{self.repo_owner}/{self.repo_name}/{self.current_branch}/{file_path}"
            else:  # Gitee
                # 对于Gitee，使用原始内容URL
                raw_url = f"https://gitee.com/{self.repo_owner}/{self.repo_name}/raw/{self.current_branch}/{file_path}"
            
            response = requests.get(raw_url, headers=self.headers, stream=True)
            
            if response.status_code == 200:
                # 获取文件大小（如果可用）
                total_size = int(response.headers.get('content-length', 0))
                downloaded = 0
                
                # 使用流式下载
                with open(save_path, 'wb') as f:
                    for chunk in response.iter_content(chunk_size=8192):
                        if chunk:
                            f.write(chunk)
                            downloaded += len(chunk)
                            
                            # 如果知道总大小，更新进度
                            if total_size > 0:
                                progress = int((downloaded / total_size) * 100)
                                # 每1%更新一次UI，避免过于频繁的更新
                                if progress % 1 == 0:
                                    self.root.after(0, lambda p=progress, n=file_name: 
                                                   self.status_var.set(f"下载 {n}: {p}%"))
                                    self.root.after(0, self.root.update_idletasks)
                return
            
            # 如果直接下载失败，尝试使用Git API
            if platform == "GitHub":
                # GitHub特有的Git Data API方法
                self.status_var.set(f"尝试通过Git API下载: {os.path.basename(file_path)}")
                self.root.update_idletasks()
                
                # 获取文件的Git引用
                api_url = f"https://api.github.com/repos/{self.repo_owner}/{self.repo_name}/git/trees/{self.current_branch}?recursive=1"
                
                response = requests.get(api_url, headers=self.headers)
                if response.status_code != 200:
                    # 检查是否是API速率限制
                    if response.status_code == 403 and 'rate limit exceeded' in response.text.lower():
                        raise Exception("GitHub API速率限制已达到，请稍后再试或添加GitHub令牌")
                    raise Exception(f"获取Git树失败: HTTP {response.status_code}")
                
                tree = response.json()
                
                # 检查是否截断（大型仓库可能会被截断）
                if tree.get('truncated', False):
                    raise Exception("仓库太大，Git树被截断。请尝试直接克隆仓库或下载ZIP文件。")
                
                # 查找文件的SHA
                file_sha = None
                for item in tree['tree']:
                    if item['path'] == file_path:
                        file_sha = item['sha']
                        break
                
                if not file_sha:
                    raise Exception(f"找不到文件的Git引用: {file_path}")
                
                # 获取文件的原始内容
                blob_url = f"https://api.github.com/repos/{self.repo_owner}/{self.repo_name}/git/blobs/{file_sha}"
                
                response = requests.get(blob_url, headers=self.headers)
                if response.status_code != 200:
                    if response.status_code == 403 and 'rate limit exceeded' in response.text.lower():
                        raise Exception("GitHub API速率限制已达到，请稍后再试或添加GitHub令牌")
                    raise Exception(f"获取文件内容失败: HTTP {response.status_code}")
                
                blob = response.json()
                
                # 检查编码
                encoding = blob.get('encoding')
                if encoding != 'base64':
                    raise Exception(f"不支持的文件编码: {encoding}")
                
                # 解码Base64内容
                file_content = base64.b64decode(blob['content'])
                
                with open(save_path, 'wb') as f:
                    f.write(file_content)
            else:  # Gitee
                # Gitee没有类似的Git Data API，尝试使用其他方法
                # 尝试使用Gitee的下载ZIP功能
                raise Exception(f"无法下载大文件: {file_name}。Gitee API不支持大文件下载，请尝试克隆仓库或从浏览器下载。")
                
        except Exception as e:
            # 如果上述方法都失败，提供有用的错误信息
            error_msg = str(e)
            platform_name = self.current_platform.get()
            
            if "rate limit exceeded" in error_msg.lower():
                error_msg += f"\n\n提示：添加{platform_name}令牌可以提高API速率限制。"
            elif "too large" in error_msg.lower() or "大文件" in error_msg:
                if platform == "GitHub":
                    error_msg += f"\n\n提示：您可以尝试直接从浏览器下载此文件：\nhttps://github.com/{self.repo_owner}/{self.repo_name}/raw/{self.current_branch}/{file_path}"
                else:  # Gitee
                    error_msg += f"\n\n提示：您可以尝试直接从浏览器下载此文件：\nhttps://gitee.com/{self.repo_owner}/{self.repo_name}/raw/{self.current_branch}/{file_path}"
            
            raise Exception(error_msg)
    
    def display_file_content(self, file_info):
        # 显示单个文件的内容
        file_name = file_info['name']
        file_type = file_info['type']
        file_size = self.format_size(file_info['size'])
        download_url = file_info.get('download_url', '')
        
        # 清空文件列表
        for item in self.file_tree.get_children():
            self.file_tree.delete(item)
        
        # 添加文件信息
        self.file_tree.insert("", tk.END, values=(file_name, "文件", file_size, "-"),
                             tags=(file_type, file_info['path'], download_url))
        
        # 更新状态
        self.status_var.set(f"已加载文件: {file_name}")
        
        # 更新返回按钮状态
        self.back_button['state'] = tk.NORMAL

def main():
    root = tk.Tk()
    app = RepoDownloader(root)
    root.mainloop()

if __name__ == "__main__":
    main()